package com.security.customer.filter;

import com.security.common.code.model.ValidateCodeType;
import com.security.common.properties.SecurityProperties;
import com.security.customer.exception.ValidateCodeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @Author: TongRui乀
 * @Date: 2020/3/21 10:40
 * @description：  图形验证码过滤器
 */
@Slf4j
@Component
public class ValidateCodeAuthenticationFilter extends OncePerRequestFilter implements InitializingBean {

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private SecurityProperties securityProperties;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    private Set<String> urls = new HashSet<>();

    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();

        String[] strings = StringUtils.split(securityProperties.getCode().getImage().getUrl(), ",");

        urls.addAll(Arrays.asList(strings));
        urls.add("/authentication/form");
        urls.addAll(Arrays.asList(StringUtils.split(securityProperties.getCode().getSms().getUrls(), ",")));
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        // 判断只过滤登录请求
        Boolean flag = false;
        for (String url : urls) {
            if(antPathMatcher.match(url, request.getRequestURI())){
                flag = true;
            }
        }
        if(flag){
            try{
                // 校验验证码
                validate(new ServletWebRequest(request, response));
            }catch (ValidateCodeException e){
                this.authenticationFailureHandler.onAuthenticationFailure(request,response,e);
                return;
            }
        }
        filterChain.doFilter(request, response);
    }

    /**
     *  校验验证码的逻辑
     * @param request
     */
    private void validate(ServletWebRequest request) {

        // session中的code
        String sessionId = request.getSessionId();

        // 如何获取 是图形验证码 还是 短信验证码类型
        ValidateCodeType validateCodeType = getValidateCodeType(request);

        String key = validateCodeType.getParamNameOnValidate() + "_" + sessionId;

        String codeInSession = this.stringRedisTemplate.opsForValue().get(key);

        // 请求中的code
        String codeInRequest;

        codeInRequest = request.getParameter(validateCodeType.getParamNameOnValidate());

        log.info("当前SessionId:{}请求中的验证码：{}, 缓存中的验证码：{}", sessionId, codeInRequest, codeInSession);

        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidateCodeException("验证码的值不能为空");
        }

        if (StringUtils.isBlank(codeInSession)) {
            throw new ValidateCodeException("验证码已过期");
        }

        if (!StringUtils.equals(codeInSession, codeInRequest)) {
            throw new ValidateCodeException("验证码不匹配");
        }

        // 清理缓存
        this.stringRedisTemplate.delete(key);

    }

    // todo  待优化
    protected ValidateCodeType getValidateCodeType(ServletWebRequest request){

        String requestURI = request.getRequest().getRequestURI();

        if(requestURI.contains(securityProperties.getCode().getSms().getUrls())){
            return ValidateCodeType.valueOf("SMS");
        }else if(requestURI.contains(securityProperties.getCode().getImage().getUrl())){
            return ValidateCodeType.valueOf("IMAGE");
        }
        return null;
    }

}
